import type { FetchOptions } from 'ofetch'
import type { UseFetchOptions } from 'nuxt/app'
import { cryptoUtils } from '~/utils/crypto/core'
import { decryptData, encryptData } from '~/utils/crypto'
import { CODE_INVALID_AUTH, CODE_SYSTEM_MAINTENANCE } from '~/enums/code'
import Cache from '~/utils/cache'
import { MessageEle } from '~/plugins/message.client'

interface HttpOptions<T> extends UseFetchOptions<T> {
  url: string
}

export interface HttpResponse<T = unknown> {
  code: number
  msg: string
  data: T
}

class HttpRequest {
  private appKey: string = import.meta.env.VITE_APP_KEY

  // private blobParseType: string = 'application/json'

  async request<T = unknown>(options: HttpOptions<T>) {
    const defaultOptions: UseFetchOptions<T> = {
      timeout: 1,
      retry: 0,
      // baseURL: '/',
      baseURL: import.meta.env.VITE_APP_BASE_API,
      headers: {
        'X-Request-Token': Cache.token() as string,
        'Content-Type': 'application/json',
        'ob-client': import.meta.env.VITE_APP_CLIENT,
        'ob-application': import.meta.env.VITE_APP_APPLICATION,
        'ob-secret-version': import.meta.env.VITE_APP_SECRET_VERSION,
        'Merchant-Id': import.meta.env.VITE_APP_MERCHANT_ID
      },
      onRequest: ({ options }) => {
        options.headers = new Headers(options.headers)
        if (this.cryptoCheck()) {
          if (['dev', 'test'].includes(import.meta.env.VITE_APP_ENV_FLAG)) {
            options.headers.set('ob-encrypted', 'true')
          }
          options = this.encryptReqData(options)
        } else {
          options.headers.set('ob-encrypted', 'false')
          options.headers.set('ob-nonce', cryptoUtils.createNonce().toString())
          options.headers.set('ob-timestamp', cryptoUtils.createTimestamp().toString())
        }
      },
      onResponse: async ({ response }) => {
        response._data = this.decryptResData(response._data)
        if (response.ok && response._data.code !== 200) {
          if (CODE_INVALID_AUTH.includes(response._data.code)) {
            // 处理登出
          }
          return Promise.reject(response._data)
        }
      },
      onResponseError: async ({ response }) => {
        if (response) {
          if (response._data) {
            response._data = this.decryptResData(response._data)
          }
          // 停服提示
          if (response._data?.code === CODE_SYSTEM_MAINTENANCE) {
            // 处理登出
          }
        }
      }
    }

    return useAsyncData(options.url, () =>
      $fetch(options.url, useMerge(options, defaultOptions)).catch((error) => {
        MessageEle.error(error.msg)
      })
    )
  }

  get<T = unknown>(options: HttpOptions<T>) {
    return this.request<T>({
      ...options,
      method: 'GET'
    })
  }

  post<T = unknown>(options: HttpOptions<T>) {
    return this.request<T>({
      ...options,
      method: 'POST'
    })
  }

  private cryptoCheck() {
    // return true
    return import.meta.env.PROD && import.meta.env.VITE_APP_ENCRYPTED === '1'
  }

  private decryptResData(data: any) {
    if (data && this.cryptoCheck()) {
      data = decryptData(data)
    }
    return data
  }

  private encryptReqData(options: FetchOptions) {
    const encryptedData = encryptData(options.body)
    const nonce = cryptoUtils.createNonce().toString()
    const timestamp = cryptoUtils.createTimestamp().toString()
    options.body = encryptedData
    options.headers = new Headers(options.headers)
    options.headers.set('ob-sign', cryptoUtils.createSign(encryptedData, nonce, timestamp, this.appKey))
    options.headers.set('ob-nonce', nonce)
    options.headers.set('ob-timestamp', timestamp)
    return options
  }
}

export default new HttpRequest()
